module FinalProject(SW, KEY, LEDR, GPIO_0, GPIO_1, CLOCK_50);

input CLOCK_50;
input [17:0] SW;
output reg [17:0] LEDR;
input [3:0] KEY;
input [35:0] GPIO_0;
output reg [35:0] GPIO_1;
reg [65:0] code;
wire [25:0] badSignal;
// counters
reg [14:0] sampleCounter;
reg sampleClock;
reg [6:0] counterIndex=7'd0;
reg [18:0] waitCounter;
//reg [11:0] noiseCount = 11'd0;
//reg [13:0] noiseDelay = 14'd0;
reg past_bit=1'b1;
reg [6:0] noiseCount;
reg [13:0] noiseClock;
// state machine registers
reg [1:0] S;
reg [1:0] NS;
reg noise_flag=1'b1;
reg [200:0] window;
reg [3:0] loop=4'd0;
reg initialize=1'b1;
reg[13:0] shitTimer=14'd9350;
reg[17:0] delayTimer=18'd0;
reg[14:0] goodTimer;
reg[23:0] waitToSend=24'd0;
reg[9:0] shitCount=10'd0;
reg [6:0] index=7'd0;
reg[2:0] dataSignal=3'b001;

reg enableFlagThing=1'b1;
reg[23:0] transmitDelayCounter = 24'b0;

// states for FSM
parameter 	IDLE = 2'b00,
				WAIT = 2'b01,
				SAMPLE = 2'b10,
				STOP = 2'b11;

// next-state logic
always @(*)
begin
	if(~KEY[0]) // reset
		NS = IDLE;
	else
	begin
		case(S)
			IDLE:
			begin

				if((&window[198:80]) && (~|window[16:0])) //black voodoo magic
				begin
					NS = WAIT;
				end
				else
				begin
					NS = IDLE;
				end
				
				
			end
			
			WAIT:
			begin
					NS = SAMPLE;
				//else
					//NS = WAIT;
			end
			
			SAMPLE:
			begin
				if(counterIndex == 66)
					NS = STOP;
				else
					NS = SAMPLE;
			end
			STOP:
			begin
				NS = STOP;
			end
			default:
				NS = IDLE;
		endcase
	end
end

// output
always @(*)
begin
	case(S)
		IDLE:
			LEDR[2:0] = 3'b001;
			
		WAIT:
			LEDR[2:0] = 3'b010;
			
		SAMPLE:
		begin
			LEDR[2:0] = 3'b100;
		end
		
		default:
			LEDR[2:0] = 3'b111;
	endcase
	
	GPIO_1[1] = SW[17];
	LEDR[17:13] = code[4:0];
	LEDR[11:7] = code[65:61	];
end


always @(posedge CLOCK_50 or negedge KEY[0])
begin
	if(~KEY[0]) // reset
	begin
		S <= IDLE;
		window=window^window; //zeros out registers
		code=code^code;
		shitTimer<=14'd9350;
			delayTimer<=18'd0;
			goodTimer<=0;
			waitToSend<=24'd0;
			shitCount<=10'd0;
			index<=7'd0;
			dataSignal<=3'b001;
	end
	else
	begin
		S <= NS;
		// counters, variable assignments, etc go here
		if(S == IDLE)
		begin
			//GPIO_1[1] <= 1'b0;
			sampleCounter <= 0;
			counterIndex <= 0;
			waitCounter <= 0;
			shitTimer <= 0;
			shitCount <= 0;
			goodTimer <= 0;
			delayTimer <= 0;
			index <= 0;
			dataSignal <= 3'b001;
			initialize <= 1'b1;
			noiseClock <= noiseClock + 1'd1;
			if(noiseClock == 14'd5000)
			begin
				window <= window << 1;
				window[0] <= GPIO_0[0];
				//GPIO_1[0] <= ~GPIO_1[0];
				//noiseCount<=noiseCount+1'd1;
				noiseClock<=14'd0;
			end
			else
			begin
				noiseCount<=0;
			end
		end
		
		if(S == WAIT)
		begin
			waitCounter <= waitCounter + 1;
		end
		
		if(S == SAMPLE)
		begin
			if(sampleCounter == 28910)
			begin
				code[counterIndex] <= GPIO_0[0];
				counterIndex <= counterIndex + 1;
				sampleCounter <= 0;
				//GPIO_1[0] = ~GPIO_1[0];
			end
			else
			begin
				sampleCounter <= sampleCounter + 1;
			end
		end
		
		// transmit logic
		if(S == STOP && SW[17]==1'b1)
		begin
			//GPIO_1[1] <= 1;
			// delay between data transmissions
			if(transmitDelayCounter < 24'd0000000) // 8000000
			begin
				transmitDelayCounter <= transmitDelayCounter + 1;
				//GPIO_1[1] <= 1;
			end
			else
			begin
				// some more delay
				if(waitToSend < 24'd8000000)
				begin
					waitToSend<=waitToSend+1'd1;
				end
			
				else
				begin
					// if the first frame aka shit data hasn't been sent yet
					if(shitCount < 10'd24) //The 12 high bits
					begin
						if(shitTimer == 14'd9350)
						begin
							GPIO_1[0] <= ~GPIO_1[0];
							shitCount <= (shitCount + 1'b1);
							shitTimer <= 14'd0;
						end
						else
						begin
							shitTimer <= (shitTimer + 1'b1);
						end
					end //end else
					
					// if the first frame has been sent but it isn't time to send the signal yet
					else if((delayTimer < 18'd90001)/* && (shitCount == 10'd24)*/) //The period of 0 between signals
					begin
						GPIO_1[0] <= 1'b0;
						delayTimer <= delayTimer + 1'd1;
					end
					
					// signal sending
					else if((delayTimer > 18'd90000) && (index < 66)) //The data signal
					begin
						if(code[index] == 1'b0) //if data bit is 0
						begin
							if(dataSignal == 3'b001) //the first 3rd of a data bit
							begin
								GPIO_1[0] <= 1'b1;
								if(goodTimer == 14'd9350)
								begin
									dataSignal <= (dataSignal << 1);
									goodTimer <= 14'd0;
								end
								else
								begin
									goodTimer <= (goodTimer + 1'b1);
								end
							end
							else if(dataSignal == 3'b010 || dataSignal == 3'b100)
							begin
								GPIO_1[0] <= 1'b0;
								if(goodTimer == 14'd9350)
								begin
									dataSignal <= (dataSignal << 1);
									goodTimer <= 14'd0;
								end
								else
								begin
									goodTimer <= (goodTimer + 1'b1);
								end
							end
							else
							begin
								dataSignal <= 3'b001;
								index <= (index + 1'b1);
							end
					
						end
						else //if the data bit is 1
						begin
							if(dataSignal == 3'b001 || dataSignal == 3'b010) //the first and second 3rd of a data bit
							begin
								GPIO_1[0] <= 1'b1;
								if(goodTimer == 14'd9350)
								begin
									dataSignal <= (dataSignal << 1);
									goodTimer <= 14'd0;
								end
								else
								begin
									goodTimer <= (goodTimer + 1'b1);
								end
							end
							else if(dataSignal == 3'b100) // the third third lol
							begin
								GPIO_1[0] <= 1'b0;
								if(goodTimer == 14'd9350)
								begin
									dataSignal <= (dataSignal << 1);
									goodTimer <= 14'd0;
								end
								else
								begin
									goodTimer <= (goodTimer + 1'b1);
								end
							end
							else
							begin
								dataSignal <= 3'b001;
								index <= (index + 1'b1);
							end
				
						end
						
						
					end
					else if(index > 65) // reset
					begin
						index <= 0;
						goodTimer <= 0;
						delayTimer <= 0;
						transmitDelayCounter <= 0;
						waitToSend <= 0;
						dataSignal <= 3'b001;
						shitCount <= 0;
						shitTimer <= 0;
					end
			
				end // end of transmit logic block
			end
		end
		else
		begin
			//GPIO_1[0]<=~GPIO_1[0];
		end			
	end // end of non-reset block
end // end of always block

endmodule